package seminar;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * Main class
 * @author Johannes Degler
 */
public class Main {

	private static final Logger log = Logger.getLogger(Main.class);

	private static final int POINTS_TO_REACH =  -12; //-12 is default

	private static final int GENERATIONS_LIMIT = 200; //200 is default

	private static final boolean IGNORE_LIMITS = false; //For testing only, ignores points and generations limit
	
	private Main(){
		//Hide constructor
	}

	/**
	 * Return a copy of the list of semesters and their lectures
	 * @param child Child that will own the semesters
	 * @return List of semesters
	 */
	public static Set<Semester> createSemesterSet(final Child child){
		final Set<Semester> semesters = new HashSet<Semester>();

		//Inf2
		final Map<Lecture, Integer> mapInf2 = new HashMap<Lecture, Integer>();
		mapInf2.put(new Lecture("Softwareprojekt"), 2);
		mapInf2.put(new Lecture("Lineare Algebra"), 1);
		mapInf2.put(new Lecture("Internet Programmierung"), 2);
		mapInf2.put(new Lecture("Mathematik 2"), 3);
		mapInf2.put(new Lecture("Fremdsprace"), 1);
		mapInf2.put(new Lecture("Programmierung 2"), 3);
		semesters.add(new Semester("Inf2", child, mapInf2));

		//Inf4
		final Map<Lecture, Integer> mapInf4 = new HashMap<Lecture, Integer>();
		mapInf4.put(new Lecture("Compilerbau"), 2);
		mapInf4.put(new Lecture("Computernumerik"), 2);
		mapInf4.put(new Lecture("Datenbanken"), 2);
		mapInf4.put(new Lecture("Verteilte Systeme"), 4);
		mapInf4.put(new Lecture("Softwareprojektmanagement"), 1);
		mapInf4.put(new Lecture("Prozedurales Programmieren"), 1);
		semesters.add(new Semester("Inf4", child, mapInf4));

		//Inf6
		final Map<Lecture, Integer> mapInf6 = new HashMap<Lecture, Integer>();
		mapInf6.put(new Lecture("Pervasive Computing"), 2);
		mapInf6.put(new Lecture("Daten-/Systemsicherheit"), 2);
		mapInf6.put(new Lecture("Bildverarbeitung"), 2);
		mapInf6.put(new Lecture("Software-Projekt 2"), 4);
		mapInf6.put(new Lecture("Graph. Datenverarbeitung"), 2);
		semesters.add(new Semester("Inf6", child, mapInf6));

		//BW1
		final Map<Lecture, Integer> mapBw1 = new HashMap<Lecture, Integer>();
		mapBw1.put(new Lecture("Grundl. der Logistik"), 1);
		mapBw1.put(new Lecture("Grundl. der Produktionswirtsch."), 1);
		mapBw1.put(new Lecture("Wirtsch. Privatrecht"), 2);
		mapBw1.put(new Lecture("Qualitätsmanagement"), 1);
		mapBw1.put(new Lecture("Volkswirtschaftslehre"), 2);
		mapBw1.put(new Lecture("Wissenschaftl. Arbeiten"), 1);
		mapBw1.put(new Lecture("Finanzmathematik"), 1);
		mapBw1.put(new Lecture("Softwareanwendungen"), 2);
		mapBw1.put(new Lecture("Betr. Rechnungswesen"), 1);
		semesters.add(new Semester("BW1", child, mapBw1));

		//BW2
		final Map<Lecture, Integer> mapBw2 = new HashMap<Lecture, Integer>();
		mapBw2.put(new Lecture("Wirtschaftsinformatik"), 2);
		mapBw2.put(new Lecture("Grundl. der Logistik"), 2);
		mapBw2.put(new Lecture("Kosten/Leistungsrechnung"), 2);
		mapBw2.put(new Lecture("Ertragssteuern"), 1);
		mapBw2.put(new Lecture("Techn. Prozesssteuerung"), 1);
		mapBw2.put(new Lecture("Qualitätsmanagement"), 1);
		mapBw2.put(new Lecture("Wirtsch. Englisch"), 2);
		mapBw2.put(new Lecture("Statistik"), 1);
		mapBw2.put(new Lecture("Arbeitsrecht"), 1);
		mapBw2.put(new Lecture("Betr. Rechnungswesen"), 1);
		mapBw2.put(new Lecture("Personalwesen"), 2);
		semesters.add(new Semester("BW2", child, mapBw2));

		//BW3
		final Map<Lecture, Integer> mapBw3 = new HashMap<Lecture, Integer>();
		mapBw3.put(new Lecture("Spanisch 2"), 2);
		mapBw3.put(new Lecture("Marktforsch./Contr."), 2);
		mapBw3.put(new Lecture("English"), 1);
		mapBw3.put(new Lecture("Vertiefung Recht/Steuern"), 2);
		mapBw3.put(new Lecture("Beschaffung/Logistik"), 2);
		mapBw3.put(new Lecture("Controlling"), 2);
		mapBw3.put(new Lecture("Qualitätsm. u. Produktionsw."), 2);
		mapBw3.put(new Lecture("Gesellschaftsrecht"), 1);
		mapBw3.put(new Lecture("Betr. Rechnungswesen 2"), 1);
		mapBw3.put(new Lecture("Umsatzsteuer"), 1);
		semesters.add(new Semester("BW3", child, mapBw3));

		//VM1
		final Map<Lecture, Integer> mapVm1 = new HashMap<Lecture, Integer>();
		mapVm1.put(new Lecture("Geodäsie"), 3);
		mapVm1.put(new Lecture("Geodata"), 4);
		mapVm1.put(new Lecture("Messdatenanalyse"), 2);
		mapVm1.put(new Lecture("Industrievermessung"), 3);
		mapVm1.put(new Lecture("Einf. Marketing"), 2);
		semesters.add(new Semester("VM1", child, mapVm1));

		//WI2
		final Map<Lecture, Integer> mapWi2 = new HashMap<Lecture, Integer>();
		mapWi2.put(new Lecture("Programmierung 2"), 4);
		mapWi2.put(new Lecture("Projekt I"), 3);
		mapWi2.put(new Lecture("Business Englisch"), 1);
		mapWi2.put(new Lecture("Mathematik 2"), 3);
		mapWi2.put(new Lecture("Volkswirtschaftslehre"), 2);
		mapWi2.put(new Lecture("Einf. Marketing"), 2);
		semesters.add(new Semester("WI1", child, mapWi2));

		//IM2
		final Map<Lecture, Integer> mapIm2 = new HashMap<Lecture, Integer>();
		mapIm2.put(new Lecture("Bau & Immobilienwirtschaft"), 1);
		mapIm2.put(new Lecture("Projekt Management"), 1);
		mapIm2.put(new Lecture("Entwurf Verkehrsanlagen"), 2);
		mapIm2.put(new Lecture("Bauorga./Verfahrenstechnik"), 1);
		mapIm2.put(new Lecture("Kosten/Leistungsrechnugn"), 2);
		mapIm2.put(new Lecture("CAD"), 2);
		mapIm2.put(new Lecture("Bodenmech. + Erdbau"), 1);
		mapIm2.put(new Lecture("Technical English"), 1);
		mapIm2.put(new Lecture("Verkehrsplanung"), 2);
		mapIm2.put(new Lecture("Konventionelle Energien"), 2);
		semesters.add(new Semester("IM1", child, mapIm2));

		//I5
		final Map<Lecture, Integer> mapI5 = new HashMap<Lecture, Integer>();
		mapI5.put(new Lecture("Integriertes Projekt 1"), 3);
		mapI5.put(new Lecture("Gebäudetypologie"), 2);
		mapI5.put(new Lecture("Licht, Material, Raum"), 2);
		mapI5.put(new Lecture("Gestaltung"), 1);
		mapI5.put(new Lecture("Antropologie"), 2);
		mapI5.put(new Lecture("Wärme, Akustik, Solar"), 2);
		mapI5.put(new Lecture("Produktmanagement"), 1);
		mapI5.put(new Lecture("Design und Kulturgeschichte"), 1);
		semesters.add(new Semester("I5", child, mapI5));

		return semesters;
	}

	/**
	 * Start seminar project
	 * @param args Arguments (ignored)
	 */
	public static void main(final String args[]){
		final long start = System.currentTimeMillis();

		Main.start();

		//Log time needed
		Main.log.info("Done in " + (System.currentTimeMillis() - start)/1000 + " seconds");
	}

	/**
	 * Print a child on stdout
	 * @param child Child to print
	 */
	private static void printChild(final Child child) {
		System.out.println("\n\n\n------ Child: " + child + "---------");
		for(final Semester semester : child.getSemesters().values())
			Main.printSemester(semester);

		System.out.print("\n\n");
	}

	/**
	 * Print a semester on stdout
	 * @param semester Semester to print
	 */
	private static void printSemester(final Semester semester) {
		System.out.println("\n\nSemester: " + semester + "");
		for(int day = 0; day < 5; day++){
			System.out.print("\nDay " + (day+1) + ": ");
			for(int slot = 0; slot < 6; slot++) {
				final LectureEvent lectureEvent = semester.getEvents()[day][slot];
				if(lectureEvent == null)
					System.out.print(" | ---");
				else
					System.out.print(" | " + lectureEvent.getLecture().getName() + " in " + lectureEvent.getRoom().getDescription());
			}
		}
	}

	private static void start() {
		//Create initial parent generation
		Main.log.info("Creating initial parent generation");
		final Generation initialParents = new Generation(-1);
		//Initial distribution
		Main.log.info("Initial distribution");
		try {
			initialParents.initialDistribute();
		} catch (final DistributionException e) {
			Main.log.error("Error during initial distribution: " + e.getMessage(), e.getCause());
			e.printStackTrace();
		}
		//Evaluate initial parent generation
		Main.log.info("Evaluating initial parent generation");
		initialParents.evaluate();

		Generation generation = initialParents; //Current parent generation
		int points = Integer.MIN_VALUE;
		for(int i = 0; IGNORE_LIMITS || (i < Main.GENERATIONS_LIMIT && points < Main.POINTS_TO_REACH); i++){ //Repeat MAX_GENERATIONS times
			//Create next generation
			Main.log.info("Creating new generation " + (i+1));
			try {
				generation = new Generation(generation, i);
			} catch (final DistributionException e) {
				Main.log.error("Error creating new generation: " + e.getMessage() + ". Exiting...", e.getCause());
				e.printStackTrace();
				System.exit(-1);
			}

			//Validate next generation
			Main.log.info("Evaluating generation " + (i+1));
			generation.evaluate();
			Main.log.info("Generation has an average quality of " + generation.getAveragePoints() + " points");
			
			//Get points
			final Child bestChild = generation.getBestChild();
			points = bestChild.getPoints();
			Main.log.info("Best child has " + points + " points");
		}

		//Print best child on stdout
		Main.printChild(generation.getBestChild());
	}
}
